昨天,介紹完與滑鼠、觸控相關的輸入互動後,今天就要來介紹怎麼在 CG 上偵測玩家的鍵盤輸入啦~
首先,PixiJS 做為一個 2D 繪圖引擎,並不包含偵測鍵盤輸入的功能,所以正常情況下我們必須依賴瀏覽器本身的 API 來偵測鍵盤輸入。但好家在這裡是 Code.Gamelet,CG 已經幫我們把這件事情簡化了,並且結合了我們昨天介紹過的 EventEmitter ,使鍵盤的偵測方式變得和滑鼠的偵測方式相似,統一的結構在程式碼的閱覽上也會更加易讀。
Keyboard 類別Keyboard 是 CG 的核心模組 Base2 內建的類別,在開發時,我們不需要自己創建它,因為 Base2 已經幫我們把它創建好,並儲存在 keyboard 這個全域常數裡面了,我們只要從 Base2 拿出來就可以開始使用裡面的功能。
Keyboard 提供了兩個核心功能:
on() 和 off() 函數來監聽按鍵事件,這和昨天的滑鼠、觸控事件非常相似。import keyboard = CG.Base2.keyboard;
import Keyboard = CG.Base2.keyboards.Keyboard;
import KeyCode = CG.Base2.keyboards.KeyCode;
import Key = CG.Base2.keyboards.Key;
在使用 Keyboard 的時候,原則上最頂層都會有這幾個 import ,沒意外的話,在我們使用Keyboard 時,CG 就會自動幫我們把這幾行寫好了,因此這邊只是稍微提一下,待會會個別介紹它們。
但如果 CG 真的沒有幫你寫好,你可以對著你使用的物件、類別按下滑鼠右鍵打開選單,選擇「CG自動導入」即可。
監聽按鍵事件
首先,讓我們用最簡單的方式來監聽按鍵被按下的事件:
import pixi = CG.Pixi.pixi;
import keyboard = CG.Base2.keyboard;
import Keyboard = CG.Base2.keyboards.Keyboard;
import KeyCode = CG.Base2.keyboards.KeyCode;
async function start() {
// 初始化 Pixi
await pixi.initialize({ stageWidth: 960, stageHeight: 540 });
// 建立文字物件
const text = new PIXI.Text({
text: "試著按點什麼",
style: { fill: 0xFFFFFF, fontSize: 100, align: "center" },
anchor: 0.5,
position: { x: pixi.stageWidth * 0.5, y: pixi.stageHeight * 0.5 }
} as PIXI.TextOptions);
// 將文字物件加入舞台
pixi.root.addChild(text);
// 監聽鍵盤事件
keyboard.on(Keyboard.EVENT.PRESSED, (key: KeyCode, event: KeyboardEvent) => {
text.text = `按下了 ${event.code}`;
});
}
start();
這段程式碼使用 keyboard.on() 來監聽 Keyboard.EVENT.PRESSED 這個事件。當任何按鍵被按下時,它就會觸發函數,並將按鍵的相關資訊傳遞給我們,讓我們可以根據 event.code 顯示按下的鍵名。

你也可以用 switch 語法來處理不同的按鍵,例如:
// 在 keyboard.on() 內添加,其他保持不變...
switch (key) {
case Key.Z:
text.text += "\\n發射了導向飛彈";
break;
case Key.X:
text.text += "\\n埋了高性能地雷";
break;
case Key.C:
text.text += "\\n發射了迫擊砲";
break;
case Key.V:
text.text += "\\n發射了雷射砲";
break;
}
值得注意的是,keyboard.on() 會丟出兩個參數:key 和 event。
key 是一個 KeyCode 物件,這是由 Base2 預先定義好的標準按鍵代碼,它提供了一個簡潔、統一的方式來代表按鍵(例如 Key.Z)。event 則是瀏覽器原生的 KeyboardEvent 物件,它包含了更底層的資訊,例如 event.code(按鍵在鍵盤上的物理位置代碼)。通常在遊戲實作上,我們只會需要用到 key ,但若你有其他更複雜的需求,就可以自己利用 event 來處理。
偵測按鍵狀態:isDown()
事件監聽器對於一次性的按鍵事件(例如發射子彈)非常有用。但是,如果你想讓角色在玩家按住按鍵時持續移動,單靠 on() 事件就不夠了。
這時,我們需要使用 keyboard.isDown() 這個函數。它會回傳一個布林值(true 或 false),告訴我們某個按鍵是否處於被按下的狀態。
// 在 start() 內添加,其他保持不變...
// 定義移動速度
const moveSpeed = 5;
// 設置一個循環更新函數
CG.Base2.addUpdateFunction(() => {
// 根據指定按鍵的狀態,改變 text 的座標位置
if (keyboard.isDown(Key.W)) text.y -= moveSpeed; // 向上移動
if (keyboard.isDown(Key.S)) text.y += moveSpeed; // 向下移動
if (keyboard.isDown(Key.A)) text.x -= moveSpeed; // 向左移動
if (keyboard.isDown(Key.D)) text.x += moveSpeed; // 向右移動
});
這裡我們使用了 CG.Base2.addUpdateFunction,它會讓程式碼在每一幀(Frame)都被執行。當我們在其中檢查 keyboard.isDown() 的狀態時,只要指定的按鍵被按住,程式碼就會在每一幀持續執行,從而實現了平滑、持續的移動。

今天我們介紹了如何在 CG 上處理鍵盤輸入:
keyboard.on() 來監聽一次性的按鍵事件,適合處理發射技能或切換裝備等單次動作。keyboard.isDown() 來偵測按鍵的即時狀態,並搭配遊戲的更新函數,實現玩家角色持續性的移動等遊戲邏輯。這兩種方式各有用途,而 isDown() 與循環更新的結合,正是所有遊戲實現即時控制的關鍵。
明天,我們將會更深入的介紹 CG.Base2.addUpdateFunction 這個東西,雖然之前在 Day 04 介紹了它會不斷執行的功能,但還有許多細節沒有提到,像是不同設備之間的畫面更新率(FPS)不同,更新的頻率不同,該如何同步之類的,不過這些就等到明天再說吧!